/*!
\copyright  Copyright (c) 2020 - 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
*/

#ifdef INCLUDE_LATENCY_MANAGER

#include "kymera_dynamic_latency.h"
#include "kymera_latency_manager.h"
#include "kymera_internal_msg_ids.h"
#include "kymera_data.h"
#include <bt_device.h>
#include <logging.h>
#include <bdaddr.h>
#include <acl.h>
#include <mirror_profile.h>
#include <hdma_core.h>

/*! \brief Dynamic latency state */
typedef struct
{
    /*! Current dynamic latency in milli-seconds */
    uint16 dynamic_latency;

    /*! The base latency for the current use case in milli-seconds */
    uint16 base_latency;

    /*! This flag tells if Dynamic Latency Adjustment feature is enabled. This
        flag is disabled by default. Unless it is set latency will not be
        dynamically updated. */
    unsigned dynamic_adjustment_enabled : 1;

    /*! This flag is set when dynamic adjustment is currently active */
    unsigned dynamic_adjustment_active : 1;

} kymera_dynamic_latency_data_t;

kymera_dynamic_latency_data_t kymera_dynamic_latency_data;

#define KymeraGetDynamicLatencyData() (&kymera_dynamic_latency_data)

void Kymera_DynamicLatencyInit(void)
{
    kymera_dynamic_latency_data_t *data = KymeraGetDynamicLatencyData();
    memset(data, 0, sizeof(*data));
}

void Kymera_DynamicLatencyEnableDynamicAdjustment(void)
{
    KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled = 1;
}

void Kymera_DynamicLatencyDisableDynamicAdjustment(void)
{
    KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled = 0;
}

bool Kymera_DynamicLatencyIsEnabled(void)
{
#if defined(INCLUDE_MIRRORING) && !defined(ENABLE_TWM_STEREO)
    return  BtDevice_IsMyAddressPrimary() &&
            KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled &&
            MirrorProfile_IsA2dpActive();
#endif /* defined(INCLUDE_MIRRORING) && !defined(ENABLE_TWM_STEREO) */

#if defined(INCLUDE_STEREO) && !defined(ENABLE_TWM_STEREO)
    return KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled;
#endif /* defined(INCLUDE_STEREO) && !defined(ENABLE_TWM_STEREO) */

#ifdef ENABLE_TWM_STEREO
    kymeraTaskData *theKymera = KymeraGetTaskData();
    if(appKymera_IsStereoMirroring(theKymera))
    {
        return  BtDevice_IsMyAddressPrimary() &&
                KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled &&
                MirrorProfile_IsA2dpActive();
    }
    else
    {
        return KymeraGetDynamicLatencyData()->dynamic_adjustment_enabled;
    }
#endif /*ENABLE_TWM_STEREO*/
}

void Kymera_DynamicLatencyStartDynamicAdjustment(uint16 base_latency)
{
    Kymera_DynamicLatencyEnableDynamicAdjustment();
    if (Kymera_DynamicLatencyIsEnabled())
    {
        kymera_dynamic_latency_data_t *data = KymeraGetDynamicLatencyData();
        DEBUG_LOG("Kymera_DynamicLatencyStartDynamicAdjustment");

        data->base_latency = data->dynamic_latency = base_latency;
        data->dynamic_adjustment_active = 1;
        Kymera_DynamicLatencyHandleLatencyTimeout();
    }
}

void Kymera_DynamicLatencyStopDynamicAdjustment(void)
{
    kymera_dynamic_latency_data_t *data = KymeraGetDynamicLatencyData();
    DEBUG_LOG("Kymera_DynamicLatencyStopDynamicAdjustment");
    MessageCancelAll(KymeraGetTask(), KYMERA_INTERNAL_LATENCY_CHECK_TIMEOUT);
    data->dynamic_adjustment_active = 0;
}

uint16 Kymera_DynamicLatencyMarshal(uint8 *buf, uint16 length)
{
    uint16 written = 0;
    size_t size = sizeof(kymera_dynamic_latency_data_t);
    if (length >= size)
    {
        memcpy(buf, KymeraGetDynamicLatencyData(), size);
        written = size;
    }
    return written;
}

uint16 Kymera_DynamicLatencyUnmarshal(const uint8 *buf, uint16 length)
{
    uint16 read = 0;
    size_t size = sizeof(kymera_dynamic_latency_data_t);
    if (length >= size)
    {
        memcpy(KymeraGetDynamicLatencyData(), buf, size);
        read = size;
    }
    return read;
}

static void Kymera_DynamicLatencyScheduleNextMeasurement(void)
{
    MessageCancelFirst(KymeraGetTask(), KYMERA_INTERNAL_LATENCY_CHECK_TIMEOUT);
    MessageSendLater(KymeraGetTask(), KYMERA_INTERNAL_LATENCY_CHECK_TIMEOUT,
                        NULL, KYMERA_LATENCY_CHECK_DELAY);
}

void Kymera_DynamicLatencyHandoverCommit(bool is_primary)
{
    if (is_primary)
    {
        if (Kymera_DynamicLatencyIsEnabled())
        {
            if (KymeraGetDynamicLatencyData()->dynamic_adjustment_active)
            {
                Kymera_DynamicLatencyScheduleNextMeasurement();
            }
        }
    }
    else
    {
        Kymera_DynamicLatencyStopDynamicAdjustment();
    }
}

void Kymera_DynamicLatencyHandleLatencyTimeout(void)
{
    uint16 calculated_latency = KYMERA_LATENCY_DEFAULT;
    kymera_dynamic_latency_data_t * data = KymeraGetDynamicLatencyData();

#ifdef INCLUDE_HDMA_LINK_QUALITY_EVENT
    uint16 lq = Hdma_getLowestLinkQuality();


    if(lq != 0xffff)
    {
        DEBUG_LOG_VERBOSE("Kymera_DynamicLatencyHandleLatencyTimeout Lowest_LQ %d",lq);

        if(lq < LINK_QUALITY_AVERAGE && lq > LINK_QUALITY_LOW)
        {
            calculated_latency = KYMERA_LATENCY_INCR_100MS;
        }
        else if (lq <= LINK_QUALITY_CRITICAL)
        {
            calculated_latency = KYMERA_LATENCY_INCR_200MS;
        }
        else
        {
            /* Nothing */
        }
    }

#endif    

    if (calculated_latency && calculated_latency != data->dynamic_latency)
    {
		DEBUG_LOG_VERBOSE("Kymera_DynamicLatencyHandleLatencyTimeout Calculated_Latency %d",calculated_latency);
        data->dynamic_latency = calculated_latency;
        Kymera_LatencyManagerAdjustLatency(data->dynamic_latency);
    }

    Kymera_DynamicLatencyScheduleNextMeasurement();
}

#endif /* INCLUDE_LATENCY_MANAGER */
